package com.gitbl.webchat.db;

import com.gitbl.webchat.db.bean.UserBean;
import com.gitbl.webchat.db.mapper.UserMapper;
import com.gitbl.webchat.server.Application;
import org.apache.ibatis.mapping.Environment;
import org.apache.ibatis.session.Configuration;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import org.apache.ibatis.transaction.TransactionFactory;
import org.apache.ibatis.transaction.jdbc.JdbcTransactionFactory;
import org.springframework.cglib.proxy.Enhancer;
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;

import javax.sql.DataSource;
import java.io.InputStream;
import java.lang.reflect.Method;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Properties;

public class DbUtils {
    SqlSessionFactory sf;
    SqlSession readSession;
    public static Properties properties;

    public static SqlSessionFactory newSqlSessionFactory(String config) {
        InputStream is = Application.class.getClassLoader().getResourceAsStream(config);

        SqlSessionFactory sessionFactory = new SqlSessionFactoryBuilder().build(is, properties);
        Configuration configuration = sessionFactory.getConfiguration();
        initConfiguration(configuration);
        return sessionFactory;
    }

    static void initConfiguration(Configuration configuration) {
        configuration.setLazyLoadingEnabled(true);
        configuration.getTypeAliasRegistry().registerAlias(UserBean.class);
        configuration.addMapper(UserMapper.class);
    }

    public static SqlSessionFactory newSqlSessionFactory(DataSource ds) {
        TransactionFactory transactionFactory = new JdbcTransactionFactory();
        Environment environment = new Environment("development", transactionFactory, ds);
        Configuration configuration = new Configuration(environment);
        initConfiguration(configuration);
        return new SqlSessionFactoryBuilder().build(configuration);
    }

    public DbUtils(SqlSessionFactory sf) {
        this.sf = sf;
        this.readSession = sf.openSession();
    }

    public DbUtils(String configpath) {
        this.sf = DbUtils.newSqlSessionFactory(configpath);
        this.readSession = sf.openSession();
    }

    public DbUtils(DataSource ds) {
        this.sf = DbUtils.newSqlSessionFactory(ds);
        this.readSession = sf.openSession();
    }

    public <T> T getSingleWriter(final Class<T> tp) {
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(tp);
        enhancer.setCallback(new MethodInterceptor() {
            public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
                SqlSession s = sf.openSession();
                try {
                    Object cl = s.getMapper(tp);
                    Object result = method.invoke(cl, args);
                    s.commit();
                    return result;
                } finally {
                    s.close();
                }
            }
        });
        return (T) enhancer.create();
    }

    public <T> T getSingleReader(final Class<T> tp) {
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(tp);
        enhancer.setCallback(new MethodInterceptor() {
            public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
                SqlSession s = sf.openSession();
                try {
                    Object cl = s.getMapper(tp);
                    Object result = method.invoke(cl, args);
                    return result;
                } finally {
                    s.close();
                }

            }
        });
        return (T) enhancer.create();
    }

    public class DbTransaction {
        DbTransaction() {
            session = sf.openSession();
        }

        SqlSession session;

        public <T> T getMapper(final Class<T> tp) {
            return session.getMapper(tp);
        }

        public void commitAndClose() {
            session.commit();
            session.close();
        }

        public void rollbackAndClose() {
            session.rollback();
            session.close();
        }
    }

    public DbTransaction begin() {
        return new DbTransaction();
    }


    public static String getMD5(String plainText) {
        try {
            MessageDigest md = MessageDigest.getInstance("MD5");//获取MD5实例
            md.update(plainText.getBytes());//此处传入要加密的byte类型值
            byte[] digest = md.digest();//此处得到的是md5加密后的byte类型值

            /*
               下边的运算就是自己添加的一些二次小加密，记住这个千万不能弄错乱，
                   否则在解密的时候，你会发现值不对的（举例：在注册的时候加密方式是一种，
                在我们登录的时候是不是还需要加密它的密码然后和数据库的进行比对，但是
            最后我们发现，明明密码对啊，就是打不到预期效果，这时候你就要想一下，你是否
             有改动前后的加密方式）
            */
            int i;
            StringBuilder sb = new StringBuilder();
            for (int offset = 0; offset < digest.length; offset++) {
                i = digest[offset];
                if (i < 0)
                    i += 256;
                if (i < 16)
                    sb.append(0);
                sb.append(Integer.toHexString(i));//通过Integer.toHexString方法把值变为16进制
            }
            return sb.toString();//从下标0开始，length目的是截取多少长度的值
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
            return null;
        }
    }
}
